package org.cashbook.core.objects.transaction;

import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.cashbook.core.IDFactory;
import org.cashbook.core.objects.account.IAccount;
import org.cashbook.core.objects.category.ICategory;
import org.cashbook.core.objects.keyword.IKeyword;
import org.cashbook.core.objects.luxuryClass.ILuxuryClass;
import org.cashbook.entities.IUser;
import org.cashbook.utilities.UtilitiesFactory;

public abstract class AbstractTransaction implements ITransaction, Serializable {
	private static final long serialVersionUID = -6012033174144555588L;
	protected AbstractTransaction firstOccurence;
	protected AbstractTransaction nextOccurence;
	protected Date startDate;
	protected Date endDate;
	protected IAccount account;
	protected ICategory category;
	protected ICategory subCategory;
	protected Set<IKeyword> keywords = new HashSet<IKeyword>();
	protected IUser user;
	protected ILuxuryClass luxuryClass;
	protected double amount;
	protected int transactionID;
	
	public AbstractTransaction(Date startDate, Date endDate, AbstractTransaction firstOccurence, AbstractTransaction nextOccurence, IAccount account, ICategory category, ICategory subCategory, IUser user, Set<IKeyword> keywords, ILuxuryClass luxuryClass, double amount) {
		this.startDate = startDate;
		this.endDate = endDate;
		this.firstOccurence = firstOccurence;
		this.nextOccurence = nextOccurence;
		this.account = account;
		this.category = category;
		this.subCategory = subCategory;
		this.user = user;
		this.keywords = keywords;
		this.luxuryClass = luxuryClass;
		this.amount = amount;
		transactionID = IDFactory.getTransactionID();
		
		UtilitiesFactory.getLogger().fine("Transaction created [Transaction type " + getTransactionType() + "; ID " + transactionID + "]", this);
	}
	
	@Override
	public Date getStartDate() {
		UtilitiesFactory.getLogger().finest("Returning start date [Transaction ID " + transactionID + "]", this);
		return startDate;
	}

	@Override
	public boolean setStartDate(Date startDate) {
		if(endDate == null | startDate.before(endDate)) {
			UtilitiesFactory.getLogger().finest("Setting new start date [Transaction ID " + transactionID + "]", this);
			this.startDate = startDate;
			return true;
		} else {
			UtilitiesFactory.getLogger().warning("New start date could not be set [Transaction ID " + transactionID + "]", this);
			return false;
		}
	}

	@Override
	public IAccount getAccount() {
		UtilitiesFactory.getLogger().finest("Returning account [Transaction ID " + transactionID + "]", this);
		return account;
	}

	@Override
	public void setAccount(IAccount account) {
		UtilitiesFactory.getLogger().finest("Setting new account [Transaction ID " + transactionID + "]", this);
		this.account = account;
	}

	@Override
	public ICategory getCategory() {
		UtilitiesFactory.getLogger().finest("Returning category [Transaction ID " + transactionID + "]", this);
		return category;
	}

	@Override
	public void setCategory(ICategory category) {
		UtilitiesFactory.getLogger().finest("Setting new category [Transaction ID " + transactionID + "]", this);
		this.category = category;
	}

	@Override
	public Set<IKeyword> getKeywords() {
		UtilitiesFactory.getLogger().finest("Returning set of keywords [Transaction ID " + transactionID + "]", this);
		return keywords;
	}

	@Override
	public void setKeywords(Set<IKeyword> keywords) {
		UtilitiesFactory.getLogger().finest("Setting new set of keywords [Transaction ID " + transactionID + "]", this);
		this.keywords = keywords;
	}

	@Override
	public void addKeyword(IKeyword keyword) {
		UtilitiesFactory.getLogger().finest("Adding new keyword [Transaction ID " + transactionID + "]", this);
		keywords.add(keyword);
	}

	@Override
	public IUser getUser() {
		UtilitiesFactory.getLogger().finest("Returning user [Transaction ID " + transactionID + "]", this);
		return user;
	}

	@Override
	public void setUser(IUser user) {
		UtilitiesFactory.getLogger().finest("Setting new user [Transaction ID " + transactionID + "]", this);
		this.user = user;
	}

	@Override
	public ILuxuryClass getLuxuryClass() {
		UtilitiesFactory.getLogger().finest("Returning luxury class [Transaction ID " + transactionID + "]", this);
		return luxuryClass;
	}

	@Override
	public void setLuxuryClass(ILuxuryClass luxuryClass) {
		UtilitiesFactory.getLogger().finest("Setting new luxury class [Transaction ID " + transactionID + "]", this);
		this.luxuryClass = luxuryClass;
	}

	@Override
	public double getAmount() {
		UtilitiesFactory.getLogger().finest("Returning amount [Transaction ID " + transactionID + "]", this);
		return amount;
	}

	@Override
	public void setAmount(double amount) {
		UtilitiesFactory.getLogger().finest("Setting new amount [Transaction ID " + transactionID + "]", this);
		this.amount = amount;
	}

	@Override
	public boolean isRecurring() {
		UtilitiesFactory.getLogger().finest("Returning recurring value [Transaction ID " + transactionID + "]", this);
		return firstOccurence != null;
	}

	@Override
	public Date getEndDate() {
		UtilitiesFactory.getLogger().finest("Returning end date [Transaction ID " + transactionID + "]", this);
		return endDate;
	}

	@Override
	public boolean setEndDate(Date endDate) {
		if(endDate.after(startDate)) {
			UtilitiesFactory.getLogger().finest("Setting new end date [Transaction ID " + transactionID + "]", this);
			this.endDate = endDate;
			return true;
		} else {
			UtilitiesFactory.getLogger().warning("New end date could not be set [Transaction ID " + transactionID + "]", this);
			return false;
		}
	}

	@Override
	public int hashCode() {
		UtilitiesFactory.getLogger().finest("Calculating hash code [Transaction ID " + transactionID + "]", this);
		final int prime = 31;
		int result = 1;
		result = prime * result + transactionID;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		UtilitiesFactory.getLogger().finest("Comparing instance [Transaction ID " + transactionID + "]", this);
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		AbstractTransaction other = (AbstractTransaction) obj;
		if (transactionID != other.transactionID)
			return false;
		return true;
	}

	@Override
	public int getTransactionID() {
		return transactionID;
	}

	@Override
	public void setNextOccurence(ITransaction nextOccurence) {
		if(nextOccurence instanceof AbstractTransaction) {
			this.nextOccurence = (AbstractTransaction) nextOccurence;
			UtilitiesFactory.getLogger().finest("Setting new nextOccurence instance [Transaction ID " + transactionID + "]", this);
		} else 
			UtilitiesFactory.getLogger().warning("Setting of new nextOccurence failed, passed instance is not of type " + AbstractTransaction.class.getName(), this);
	}

	@Override
	public void setFirstOccurence(ITransaction firstOccurence) {
		UtilitiesFactory.getLogger().finest("Setting new firstOccurence instance [Transaction ID " + transactionID + "]", this);
		this.firstOccurence = (AbstractTransaction) firstOccurence;
	}

}
